package na.edu.polytechnic;
import java.util.ArrayList;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;

import android.app.Activity;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.os.Vibrator;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.View;

public class SpotView extends View {
   //General
   Context context;
   Vibrator v;
   Resources res = getResources();
   final int width, height; //Screen dimensions in pixels
   final float scale; //The percentage everything will be scaled

   //Image related
   Bitmap image;
   Bitmap cross;
   int offset; //Image offset so that it is centered
   ArrayList<Spot> spots;

   //Graphics related:
   Paint paint = new Paint();
   Matrix m;

   //Constructor:
   public SpotView(final Context context, final int width, final int height, final String xmlFilename) {
      super(context);
      this.context = context;
      this.width = width;
      this.height = height;
      v = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE);
      scale = height/480f; //The native resolution is 640x480. Everything will be scaled.
      scaleEverything();
      setKeepScreenOn(true);
      setClickable(true);
      setFocusable(true);

      parseXML(xmlFilename);
      cross = loadImageScaled(R.drawable.cross);
      invalidate(); //repaint
   }

   //SurfaceView methods
   public void surfaceChanged(final SurfaceHolder holder, final int format, final int width, final int height) {}
   public void surfaceCreated(final SurfaceHolder holder) {}
   public void surfaceDestroyed(final SurfaceHolder holder) {
      boolean retry = true;
      while (retry) {
         try {
            kill();
            retry = false;
         } catch (final InterruptedException e) {}
      }
   }

   private void scaleEverything() {
      if (scale == 1) //If we are working with the native resolution, scale nothing.
         return;

      Spot.scale = scale;
      Spot.radius *= scale;
      m = new Matrix();
      m.setScale(scale, scale);
   }

   private void parseXML(final String xmlFilename) {
      XMLHandlerSpot xmlHandler;
      //Parse throught xml and save the stuff we need
      try {
         /** Handling XML */
         final SAXParserFactory spf = SAXParserFactory.newInstance();
         final SAXParser sp = spf.newSAXParser();
         final XMLReader xr = sp.getXMLReader();

         /** Create handler to handle XML Tags ( extends DefaultHandler ) */
         xmlHandler = new XMLHandlerSpot();
         xr.setContentHandler(xmlHandler);

         xr.parse(new InputSource(res.openRawResource(res.getIdentifier("raw/"+xmlFilename, null, context.getPackageName()))));

         spots = xmlHandler.spots;
         //Load and scale the image:
         image = loadImageScaled(res.getIdentifier("drawable/"+xmlHandler.imageFile, null, context.getPackageName()));
         offset = (width-image.getWidth())/2; //Calculate the offset for centering the image
      }catch (final Exception e) {
         System.out.println("XML Pasing Excpetion = " + e);
      }
   }

   private Bitmap loadImageScaled(final int resid) {
      Bitmap temp = BitmapFactory.decodeResource(res, resid); //Open the bitmap
      //Scale the bitmap if necessary:
      if (scale != 1) {
         temp = Bitmap.createBitmap(temp, 0, 0, temp.getWidth(), temp.getHeight(), m, true);
         System.gc(); //Android garbage collector sucks, so we do it manually
      }
      return temp;
   }

   //Custom cleanup-method
   protected void kill() throws InterruptedException {
      setFocusable(false);
      if (image != null)
         image.recycle();
      image = null;
   }

   //Listener method for the touch screen
   @Override
   public boolean onTouchEvent(final MotionEvent m) {
      if (m.getAction() == MotionEvent.ACTION_DOWN) {
         for (final Spot p : spots) {
            if (!p.isFound && collide(m.getX(), m.getY(), p.x+offset, p.y, Spot.radius*2)) {
               p.isFound = true;
               v.vibrate(150);
            }
         }
         if (checkWin())
            ((Activity)context).showDialog(1);
         invalidate(); //Repaint
         return true;
      }
      return false;
   }

   public boolean checkWin() {
      for (final Spot p : spots) {
         if (!p.isFound)
            return false;
      }
      return true;
   }

   //Listener method for possible trackball (Override to prevent system events)
   @Override
   public boolean onTrackballEvent(final MotionEvent e) {
      return true;
   }

   /**
    * Check if the distance between two points is less than given distance.
    * @return True, if the two points were closer to each other than given distance.
    */
   public static boolean collide(final float x1, final float y1, final float x2, final float y2, final float distance) {
      final int dx = (int)(x2 - x1);
      final int dy = (int)(y2 - y1);
      if ((dx * dx) + (dy * dy) < distance*distance)
         return true;
      else
         return false;
   }

   //Drawing goes here:
   @Override
   public void onDraw(final Canvas c) {
      c.drawBitmap(image, offset, 0, paint);
      for(final Spot p : spots) {
         if (p.isFound)
            c.drawBitmap(cross, p.x+offset-cross.getWidth()/2, p.y-cross.getHeight()/2, paint);
      }
      paint.setColor(Color.RED);
   }
}